package com.codi.bus.core.service.organ.impl;

/**
 * Created by asus on 2018/5/8.
 */

import com.codi.base.util.*;
import com.codi.bus.constant.GlobalConstant;
import com.codi.bus.constant.PayTypeConst;
import com.codi.bus.constant.RuleConstant;
import com.codi.bus.core.dao.*;
import com.codi.bus.core.domain.*;
import com.codi.bus.core.event.EventBusService;
import com.codi.bus.core.event.EventTypeDef;
import com.codi.bus.core.event.model.TradeEvent;
import com.codi.bus.core.resultModel.BaseResult;
import com.codi.bus.core.service.*;
import com.codi.bus.core.service.impl.LocaldataTradeOrderService;
import com.codi.bus.core.service.model.ApplyModel;
import com.codi.bus.core.service.model.ShareTypeModel;
import com.codi.bus.core.service.model.TradeBuyModel;
import com.codi.bus.core.service.model.TradeSellModel;
import com.codi.bus.core.service.organ.OrganTradeService;
import com.codi.bus.core.service.util.ObjectCreator;
import com.codi.bus.core.vo.BuyVo;
import com.codi.bus.exception.BaseException;
import com.codi.bus.exception.SystemDBException;
import com.codi.fcloud.ufx.HsResponseUtil;
import com.codi.fcloud.ufx.constant.HSOutputParameterNames;
import com.codi.message.constant.ErrorConst;
import com.codi.message.constant.ErrorConstant;
import com.codi.message.util.MessageUtil;
import com.codi.trade.dto.HsAllotTradeDto;
import com.codi.trade.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Created by asus on 2018/5/8.
 */
@Service("organTradeService")
@Slf4j
public class OrganTradeServiceImpl implements OrganTradeService{
    @Resource(name = "transactionDao")
    private TransactionDao transactionDao;

    @Resource(name = "tradeOrderDao")
    private TradeOrderDao tradeOrderDao;

    @Resource(name = "orderFundDao")
    private OrderFundDao orderFundDao;

    @Resource(name = "userDao")
    private UserDao userDao;

    @Resource(name = "userSessionDao")
    private UserSessionDao userSessionDao;

    @Resource(name = "userBankDao")
    private UserBankDao userBankDao;

    @Resource(name = "allotTradeService")
    private AllotTradeService allotTradeService;

    @Resource(name = "redeemTradeService")
    private RedeemTradeService redeemTradeService;

    @Resource(name = "undoTradeApplyService")
    private UndoTradeApplyService undoTradeApplyService;

    @Resource(name = "queryService")
    private QueryService queryService;

    @Resource(name = "fixAllotTradeService")
    private FixAllotTradeService fixAllotTradeService;

    @Resource(name = "lookupDao")
    private LookupDao lookupDao;

    @Resource(name = "fixModifyTradeService")
    private FixModifyTradeService fixModifyTradeService;

    @Resource(name = "fastRedeemService")
    private FastRedeemService fastRedeemService;

    @Autowired
    private BankService bankService;

    @Autowired
    private TradeOrderService tradeOrderService;

    @Autowired
    private LookupService lookupService;

    @Autowired
    private BankLimitService bankLimitService;

    @Autowired
    private EventBusService eventBusService;

    @Autowired
    private MessageService messageService;

    @Autowired
    private TradeService tradeService;

    @Autowired
    private OrganBankDao organBankDao;

    @Autowired
    private LocaldataTradeOrderService localdataTradeOrderService;

    @Autowired
    private TradeOrdersDao tradeOrdersDao;

    @Autowired
    private OrganProfileDao organProfileDao;
    /**
     * 机构申购
     * @param vo
     * @return
     */
    @Override
    @Transactional
    public BaseResult organBuy(BuyVo vo, Long profileId) {
        BaseResult result = new BaseResult();
        result.setSuccess(false);

        List<OrganBank> organBanks = organBankDao.selectByTradeAccount(vo.getTradeAcco());
        OrganBank organBank = null;
        if (!organBanks.isEmpty()){
            organBank = organBanks.get(0);
        }
        if (organBank == null) {
            result.setErrorCode(ErrorConst.TRADE_ACCOUNT_NOT_FUND);
            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConst.TRADE_ACCOUNT_NOT_FUND));
            return result;
        }

        // 根据恒生银行编号
        String bankHSCode = "";
        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
        for (Lookup lookup : lookups) {
            if (lookup.getComponentValue().equals(organBank.getUbBankCode())) {
                bankHSCode = lookup.getRuleValue();
                break;
            }
        }
        if (StringUtil.isEmpty(bankHSCode)) {
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_BANK_CARD_FIND,
                ErrorConstant.PLACEHOLDER_FIELD_NO_FIND_FIELD, organBank.getUbBankCode()));
            return result;
        }

        ShareTypeModel shareTypeModel = queryService.getShareType(vo.getFundCode());
        if (shareTypeModel == null || StringUtil.isEmpty(shareTypeModel.getShareType())) {
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND));
            return result;
        }

        // 获取份额类别
        String shareType = shareTypeModel.getShareType();

        Map<String, Object> map = null;

        String errorMsg;

        String capitalMode = GlobalConstant.CAPTIAL_MODE_K;

        //线下付款 没有 最低金额  zx  2018年5月31日19:19:23

        String tradeType = GlobalConstant.TRADE_TYPE_OFFLINE_BUY;
        // 调用接口服务

        HsAllotTradeDto allotTradeDto = HsAllotTradeDto.builder()
            .capital_mode(capitalMode)
            .money_type(vo.getMoneyType().toString())
            .trade_acco(vo.getTradeAcco())
            .password(MD5.MD5Encode(vo.getTradePassword()))
            .fund_code(vo.getFundCode())
            .bank_no(bankHSCode)
            .bank_account(organBank.getUbOrganBankNo())
            .balance(vo.getFundBalance().toString())
            .share_type(shareType)
            .trade_source(vo.getChannelId())
            .build();

        map = allotTradeService.buy(allotTradeDto);



        TradeBuyModel model = new TradeBuyModel();

        Integer orderStatus = 0;
        Integer fundStatus = 0;
        String externalApplyNo = "";
        if (!HsResponseUtil.isOperationOk(map)) {
            String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
            result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
            result.setErrorMessage(errorInfo);
            // 基金状态
            fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;
            // 订单状态
            orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;
            log.error("buy - error info:{}", errorInfo);
        } else {
            result.setSuccess(true);

            // 基金状态
            fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
            // 订单状态
            orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
            // 返回的申请编号
            externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();
            model.setAllotNo(externalApplyNo);

            Date now = new Date();
            String currentTime = DateUtils.dateToTime(now);
            // 申请日期
            model.setApplyDate(map.get(HSOutputParameterNames.APPLY_DATE).toString());
            // 申请时间
            model.setApplyTime(currentTime);

            //加入交易表中
            OrganProfile organProfile = organProfileDao.qryByProfileId(profileId);
            String clientId = organProfile.getExternalClientId();
            localdataTradeOrderService.addApplyOrder(vo.getUserId().intValue(), externalApplyNo, 0, vo.getChannelId(), vo.getTradeAcco(), clientId);

            // 发生金额
            if (EqualsUtil.equals(PayTypeConst.T0, vo.getPayType())) {
                model.setBalance(vo.getFundBalance());
            } else {
                model.setBalance(BigDecimalUtil.ConvertToBigDecimal(map.get(HSOutputParameterNames.BALANCE).toString()));
            }

            // 清算日期
            model.setClearDate(map.get(HSOutputParameterNames.CLEAR_DATE).toString());
            // 下单日期
            model.setOrderDate(DateUtils.dateToSimpleStr(now));
            // 下单时间
            model.setOrderTime(currentTime);

            //
            TradeEvent event = TradeEvent.builder()
                .userId(vo.getUserId())
                .payType(vo.getPayType())
                .fundCode(vo.getFundCode())
                .balance(vo.getFundBalance())
                .allotNo(externalApplyNo)
                .count(0)
                .profileId(profileId)
                .build();
            event.setEventType(EventTypeDef.TRADE_BUY);

            eventBusService.postAsync(event);

        }

        // 订单流水号
        String orderSn = vo.getApplyNo() + vo.getUserId().longValue();
        // 插入订单数据到本地数据库
        storeData(map, capitalMode, organBank.getUbBankCode(), organBank.getUbOrganBankNo(), new BigDecimal(0),
            vo.getFundBalance(), orderSn, vo.getUserId(), vo.getChannelId(), tradeType,
            orderStatus, new Date(), externalApplyNo, vo.getFundCode(), vo.getFundName(), shareType,
            fundStatus, 0L, "", "",vo.getTradeAcco(),profileId);


        result.setResult(model);
        return result;
    }

    /**
     * 机构赎回
     * @param userId
     * @param channelId
     * @param bankAccount
     * @param capitalMode
     * @param fundName
     * @param fundCode
     * @param shares
     * @param shareType
     * @param tradePassword
     * @param applyNo
     * @param tradeAccount
     * @return
     * @throws BaseException
     */
    @Override
    @Transactional(rollbackFor = {BaseException.class})
    public BaseResult sell(Long userId, String channelId, String bankAccount, String capitalMode, String fundName,
                               String fundCode, BigDecimal shares, String shareType, String tradePassword, String applyNo, String tradeAccount, Long profileId) throws BaseException {
        List<OrganBank> organBanks = organBankDao.selectByTradeAccount(tradeAccount);
        OrganBank organBank = null;
        if (!organBanks.isEmpty()){
            organBank = organBanks.get(0);
        }
        try {
            BaseResult result = new BaseResult();
            // 调用接口服务
            Map<String, Object> map = redeemTradeService.sell(capitalMode, tradeAccount,
                MD5.MD5Encode(tradePassword), fundCode, shareType, shares, channelId);

            Integer fundStatus = 0;
            Integer orderStatus = 0;
            String externalApplyNo = "";
            if (!HsResponseUtil.isOperationOk(map)) {
                String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
                result.setErrorMessage(errorInfo);
                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;
                log.error("sell - error info:{}", errorInfo);
            } else {
                result.setSuccess(true);

                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
                // 返回的申请编号
                externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();

                //加入交易表中
                OrganProfile organProfile = organProfileDao.qryByProfileId(profileId);
                String clientId = organProfile.getExternalClientId();
                localdataTradeOrderService.addApplyOrder(userId.intValue(), externalApplyNo, 0, channelId, tradeAccount, clientId);



                TradeEvent event = TradeEvent.builder()
                    .userId(userId)
                    .payType(PayTypeConst.BANK)
                    .fundCode(fundCode)
                    .balance(shares)
                    .allotNo(externalApplyNo)
                    .build();
                event.setEventType(EventTypeDef.TRADE_SELL);

                eventBusService.postAsync(event);

            }

            // 插入数据到订单表
            // 订单流水号
            String orderSn = applyNo + userId.longValue();
            // 插入订单数据到本地数据库
            storeData(map, capitalMode, organBank.getUbBankCode(), bankAccount, shares, new BigDecimal(0),
                orderSn, userId,channelId , GlobalConstant.TRADE_TYPE_SELL, orderStatus,
                new Date(), externalApplyNo, fundCode, fundName, shareType, fundStatus,
                0L, "", ""
                ,tradeAccount, profileId);

            TradeSellModel model = new TradeSellModel();
            model.setSuccess(result.getSuccess());
            model.setFundCode(fundCode);
            model.setFundName(fundName);
            model.setAllotNo(externalApplyNo);
            Date now = new Date();
            String currentTime = DateUtils.dateToTime(now);
            // 下单日期
            model.setOrderDate(DateUtils.dateToSimpleStr(now));
            // 下单时间
            model.setOrderTime(currentTime);
            result.setResult(model);


            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(log, "sell - Exception:", exception);
            throw new SystemDBException("sell Failure!", exception);
        }
    }


    @Override
    @Transactional(rollbackFor = {BaseException.class})
    public BaseResult fastRedeemOrgan(Long userId, String channelId, String capitalMode, String tradePassword, String fundCode,
                                      String shareType, BigDecimal shares, String bankAccount, String fundName, String applyNo, String tradeAccount, Long profileId) {
        BaseResult result = new BaseResult();
        try {
            //UserBank userBank = userBankDao.getEntityByBankNo(userId, bankAccount, capitalMode);
            OrganBank userBank = organBankDao.getByProfileIdAndBankNo(profileId,bankAccount);
            // 判断是否有银行卡
            if (userBank == null || StringUtil.isEmpty(userBank.getExternalTradeAccount())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
                return result;
            }


            String bankHSCode = userBank.getUbBankNo();
            /*List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            for (Lookup lookup : lookups) {
                if (lookup.getComponentValue().equals(userBank.getBankCode())) {
                    bankHSCode = lookup.getRuleValue();
                    break;
                }
            }*/
            if (StringUtil.isEmpty(bankHSCode)) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
                result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_BANK_CARD_FIND,
                    ErrorConstant.PLACEHOLDER_FIELD_NO_FIND_FIELD, userBank.getUbBankCode()));
                return result;
            }
            // 调用恒生快速赎回接口
            Map<String, Object> map = fastRedeemService.fastRedeem(capitalMode, tradeAccount,
                MD5.MD5Encode(tradePassword), fundCode, shareType, shares, bankHSCode, bankAccount,
                channelId);


            Integer fundStatus = 0;// 基金状态
            Integer orderStatus = 0;// 订单状态
            String externalApplyNo = "";
            if (!HsResponseUtil.isOperationOk(map)) { // fix result
                String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
                result.setErrorMessage(errorInfo);
                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;
                log.error("fastRedeem - error info:{}", errorInfo);
            } else {
                result.setSuccess(true);

                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
                // 返回的申请编号
                externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();

                OrganProfile organProfile = organProfileDao.qryByProfileId(profileId);
                String clientId = organProfile.getExternalClientId();
                localdataTradeOrderService.addApplyOrder(userId.intValue(), externalApplyNo, 0, channelId, userBank.getExternalTradeAccount(), clientId);


                TradeEvent event = TradeEvent.builder()
                    .userId(userId)
                    .fundCode(fundCode)
                    .allotNo(externalApplyNo)
                    .build();
                event.setEventType(EventTypeDef.TRADE_FAST_REDEEM);
                eventBusService.postAsync(event);

                messageService.updatefastRedeemUserMsg(shares,fundName,userId,externalApplyNo,profileId);

            }

            // 插入数据到订单表
            // 订单流水号
            String orderSn = applyNo + userId.longValue();
            // 插入订单数据到本地数据库
            storeData(map, capitalMode, userBank.getUbBankCode(), userBank.getUbOrganBankNo(), shares, new BigDecimal(0),
                orderSn, userId, channelId, GlobalConstant.TRADE_TYPE_FAST_REDEEM, orderStatus,
                new Date(), externalApplyNo, fundCode, fundName, shareType, fundStatus, 0L,
                "", "",tradeAccount, profileId);
            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(log, "fast redeem - Exception:", exception);
            throw new SystemDBException("fast redeem - Failure!", exception);
        }

    }

    @Override
    public void storeData(Map<String, Object> map, String capitalMode, String bankCode, String bankAccount,
                          BigDecimal shares, BigDecimal orderBalance, String orderSn, Long userId, String channelId, String tradeType,
                          Integer orderStatus, Date applyDate, String externalApplyNo, String fundCode, String fundName,
                          String shareType, Integer fundStatus, Long protocolFixDay, String protocolPeriodUnit,
                          String externalScheduledProtocolId, String tradeAccount, Long profileId) {
        if (StringUtils.isBlank(externalApplyNo)){
            return;
        }

        // 插入数据到订单表
        TradeOrder tradeOrder = new TradeOrder();
        tradeOrder.setMoneyType(GlobalConstant.MONEY_TYPE_RMB);
        tradeOrder.setPaymentType(capitalMode);
        tradeOrder.setBankCode(bankCode);
        tradeOrder.setBankAccount(bankAccount);
        tradeOrder.setOrderBalance(orderBalance);
        tradeOrder.setOrderSn(orderSn);
        tradeOrder.setUserId(userId);
        tradeOrder.setChannelId(channelId);
        tradeOrder.setTradeType(tradeType);
        tradeOrder.setOrderStatus(orderStatus);
        tradeOrder.setApplyDate(applyDate);
        tradeOrder.setTradeAcco(tradeAccount);
        tradeOrder.setProfileId(profileId);

//        TradeOrder tradeOrder = ObjectCreator.createTradeOrder(capitalMode, bankCode, bankAccount, orderBalance,
//            orderSn, userId, channelId, tradeType, orderStatus, applyDate);
        // 插入数据到订单表
        tradeOrderDao.add(tradeOrder);

        // 订单基金表
        OrderFund orderFund = ObjectCreator.createOrderFund(tradeOrder.getOrderId(), shares, orderBalance, fundCode,
            fundName, externalApplyNo, shareType, fundStatus, protocolFixDay, protocolPeriodUnit,
            externalScheduledProtocolId);
        // 插入数据到订单基金表
        orderFundDao.add(orderFund);

        // 插入交易记录流水
        Transaction transaction = ObjectCreator.createTransaction(map);
        transaction.setOrderId(tradeOrder.getOrderId());
        // 不管成功或失败，都插入交易流水
        transactionDao.add(transaction);
    }

    @Override
    @Transactional(rollbackFor = {BaseException.class})
    public BaseResult organUndoReal(Long userId, String channelId, String originalApplyNo, String tradePassword, Long profileId) throws BaseException {
        BaseResult result = new BaseResult();
        try {
            // 判断此交易是否可以撤单
            ApplyModel applyModel = queryService.queryApply(originalApplyNo);

            if (applyModel == null) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ORDER_FIND);
                return result;
            }
            applyModel.setIsOrg(true);

            Map<String, Object> map = undoTradeApplyService.undo(originalApplyNo, MD5.MD5Encode(tradePassword),
                applyModel.getTradeAcco(), channelId);
            String externalUndoNo = "";
            if (!HsResponseUtil.isOperationOk(map)) {
                String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
                result.setErrorMessage(errorInfo);
                log.error("undo - error info:{}", errorInfo);
            } else {
                result.setSuccess(true);
                // 返回的申请编号
                externalUndoNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();

                OrganProfile organProfile = organProfileDao.qryByProfileId(profileId);
                String clientId = organProfile.getExternalClientId();
                localdataTradeOrderService.addApplyOrder(userId.intValue(), externalUndoNo, 0, channelId, applyModel.getTradeAcco(), clientId);
                TradeOrders tradeOrders = TradeOrders.builder().taconfirmFlag("4").allotNo(originalApplyNo).build();
                tradeOrdersDao.updateByApplyNo(tradeOrders);

                OrderFund orderFund = orderFundDao.getEntityByExtApplyNo(originalApplyNo);
                if (orderFund == null) {
                    log.warn("不存在的订单号={}", originalApplyNo);
                } else {
                    orderFund.setExternalUndoNo(externalUndoNo);
                    orderFund.setFundStatus(GlobalConstant.FUND_STATUS_CANCEL);
                    // 更新订单基金表
                    orderFundDao.updateStatus(orderFund);

                    TradeOrder tradeOrder = tradeOrderDao.getEntityByOrderId(orderFund.getOrderId());
                    if (tradeOrder == null) {
                        log.warn("不存在tradeOrder");
                    } else {
                        // 撤单
                        tradeOrder.setOrderStatus(GlobalConstant.ORDER_STATUS_CANCEL);
                        // 更新状态
                        tradeOrderDao.updateStatus(tradeOrder);
                    }
                }
                TradeEvent tradeEvent = TradeEvent.builder()
                    .allotNo(externalUndoNo)
                    .build();
                tradeEvent.setEventType(EventTypeDef.TRADE_UNDO);
                eventBusService.postAsync(tradeEvent);
                //插入消息
                messageService.updateUndoUserMsg(userId, originalApplyNo, profileId);
            }

            // 插入交易记录流水
            Transaction transaction = ObjectCreator.createTransaction(map);
            // 不管成功或失败，都插入交易流水
            transactionDao.add(transaction);
            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(log, "undo - Exception:", exception);
            throw new SystemDBException("undo Failure!", exception);
        }

    }
}
